// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//  File: avtTransformFilter.C
// ************************************************************************* //

#include <avtTransformFilter.h>

#include <LinearTransformAttributes.h>
#include <SimilarityTransformAttributes.h>

#include <avtLinearTransformFilter.h>
#include <avtSimilarityTransformFilter.h>
#include <avtCoordSystemConvert.h>

// ****************************************************************************
//  Method: avtTransformFilter constructor
//
//  Programmer: meredith -- generated by xml2info
//  Creation:   Mon Sep 24 13:29:16 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Thu Apr 10 11:07:48 PDT 2003
//    Initialize invM.
//
//    Hank Childs, Tue Jul  1 08:59:08 PDT 2003
//    Reflect that this filter is now a facaded filter.
//
//    Hank Childs, Tue Feb  1 16:37:56 PST 2005
//    Added coord system convert.
//
//    Jeremy Meredith, Tue Apr 15 13:17:33 EDT 2008
//    Added linear transform.
//
//    Brad Whitlock, Wed Mar 19 14:14:53 PDT 2014
//    Add callbacks to the facade filters.
//
// ****************************************************************************

avtTransformFilter::avtTransformFilter()
{
    ltf = new avtLinearTransformFilter();
    stf = new avtSimilarityTransformFilter();
    csc = new avtCoordSystemConvert();

    // Set a callback so the facaded filters can let this object make some 
    // updates to its data object info.
    ltf->SetUpdateDataObjectInfoCallback(UpdateDataObjectInfoCB, (void*)this);
    stf->SetUpdateDataObjectInfoCallback(UpdateDataObjectInfoCB, (void*)this);
    csc->SetUpdateDataObjectInfoCallback(UpdateDataObjectInfoCB, (void*)this);
}


// ****************************************************************************
//  Method: avtTransformFilter destructor
//
//  Programmer: meredith -- generated by xml2info
//  Creation:   Mon Sep 24 13:29:16 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Thu Apr 10 11:07:48 PDT 2003
//    Delete invM.
//
//    Hank Childs, Tue Jul  1 08:59:08 PDT 2003
//    Reflect that this filter is now a facaded filter.
//
//    Hank Childs, Tue Feb  1 16:37:56 PST 2005
//    Added coord system convert.
//
//    Jeremy Meredith, Tue Apr 15 13:17:33 EDT 2008
//    Added linear transform.
//
// ****************************************************************************

avtTransformFilter::~avtTransformFilter()
{
    if (ltf != NULL)
        delete ltf;
    if (stf != NULL)
        delete stf;
    if (csc != NULL)
        delete csc;
}


// ****************************************************************************
//  Method:  avtTransformFilter::Create
//
//  Programmer: meredith -- generated by xml2info
//  Creation:   Mon Sep 24 13:29:16 PST 2001
//
// ****************************************************************************

avtFilter *
avtTransformFilter::Create()
{
    return new avtTransformFilter();
}


// ****************************************************************************
//  Method:      avtTransformFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: meredith -- generated by xml2info
//  Creation:   Mon Sep 24 13:29:16 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Thu Apr 10 11:07:48 PDT 2003
//    Delete invM.
//
//    Kathleen Bonnell, Wed May 21 11:38:23 PDT 2003   
//    Check for bad axis of rotation. 
//
//    Hank Childs, Tue Jul  1 08:59:08 PDT 2003
//    Blew away any interpretation of atts and added code to make similarity
//    transform atts and pass it to that filter.
//
//    Hank Childs, Tue Feb  1 16:37:56 PST 2005
//    Also add support for coordinate transformations.
//
//    Jeremy Meredith, Tue Apr 15 13:17:33 EDT 2008
//    Added linear transform.
//
//    Jeremy Meredith, Fri Aug  7 15:32:06 EDT 2009
//    Added selectable vector transform method.
//    Made coordinate system convert filter's coordinate type enum be
//    within the filter's namespace.
//
//    Dave Pugmire, Fri May 14 08:04:43 EDT 2010
//    Flag for vector transformations.
//
//    Tom Fogal, Tue Jul 27 11:05:54 MDT 2010
//    Forward 4x4 matrix elements.
//
// ****************************************************************************

void
avtTransformFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const TransformAttributes*)a;

    if (atts.GetTransformType() == TransformAttributes::Similarity)
    {
        SimilarityTransformAttributes st_atts;
        st_atts.SetDoRotate(atts.GetDoRotate());
        st_atts.SetRotateOrigin(atts.GetRotateOrigin());
        st_atts.SetRotateAxis(atts.GetRotateAxis());
        st_atts.SetRotateAmount(atts.GetRotateAmount());
        switch (atts.GetRotateType())
        {
          case TransformAttributes::Deg:
            st_atts.SetRotateType(SimilarityTransformAttributes::Deg);
            break;
          case TransformAttributes::Rad:
            st_atts.SetRotateType(SimilarityTransformAttributes::Rad);
            break;
        }
        st_atts.SetDoScale(atts.GetDoScale());
        st_atts.SetScaleOrigin(atts.GetScaleOrigin());
        st_atts.SetScaleX(atts.GetScaleX());
        st_atts.SetScaleY(atts.GetScaleY());
        st_atts.SetScaleZ(atts.GetScaleZ());
        st_atts.SetDoTranslate(atts.GetDoTranslate());
        st_atts.SetTranslateX(atts.GetTranslateX());
        st_atts.SetTranslateY(atts.GetTranslateY());
        st_atts.SetTranslateZ(atts.GetTranslateZ());
        st_atts.SetTransformVectors(atts.GetTransformVectors());
    
        stf->SetAtts(&st_atts);
    }
    else if (atts.GetTransformType() == TransformAttributes::Coordinate)
    {
        switch (atts.GetInputCoordSys())
        {
           case TransformAttributes::Cartesian:
             csc->SetInputCoordSys(avtCoordSystemConvert::CARTESIAN);
             break;
           case TransformAttributes::Cylindrical:
             csc->SetInputCoordSys(avtCoordSystemConvert::CYLINDRICAL);
             break;
           case TransformAttributes::Spherical:
             csc->SetInputCoordSys(avtCoordSystemConvert::SPHERICAL);
             break;
        }
        switch (atts.GetOutputCoordSys())
        {
           case TransformAttributes::Cartesian:
             csc->SetOutputCoordSys(avtCoordSystemConvert::CARTESIAN);
             break;
           case TransformAttributes::Cylindrical:
             csc->SetOutputCoordSys(avtCoordSystemConvert::CYLINDRICAL);
             break;
           case TransformAttributes::Spherical:
             csc->SetOutputCoordSys(avtCoordSystemConvert::SPHERICAL);
             break;
        }


        if( atts.GetInputCoordSys()  == TransformAttributes::Cartesian &&
            atts.GetOutputCoordSys() == TransformAttributes::Cylindrical )
            csc->SetContinuousPhi(atts.GetContinuousPhi());

        switch (atts.GetVectorTransformMethod())
        {
          case TransformAttributes::None:
            csc->SetVectorTransformMethod(avtCoordSystemConvert::None);
            break;
          case TransformAttributes::AsPoint:
            csc->SetVectorTransformMethod(avtCoordSystemConvert::AsPoint);
            break;
          case TransformAttributes::AsDisplacement:
            csc->SetVectorTransformMethod(avtCoordSystemConvert::AsDisplacement);
            break;
          case TransformAttributes::AsDirection:
            csc->SetVectorTransformMethod(avtCoordSystemConvert::AsDirection);
            break;
        }
    }
    else
    {
        LinearTransformAttributes lt_atts;
        lt_atts.SetM00(atts.GetM00());
        lt_atts.SetM01(atts.GetM01());
        lt_atts.SetM02(atts.GetM02());
        lt_atts.SetM03(atts.GetM03());

        lt_atts.SetM10(atts.GetM10());
        lt_atts.SetM11(atts.GetM11());
        lt_atts.SetM12(atts.GetM12());
        lt_atts.SetM13(atts.GetM13());

        lt_atts.SetM20(atts.GetM20());
        lt_atts.SetM21(atts.GetM21());
        lt_atts.SetM22(atts.GetM22());
        lt_atts.SetM23(atts.GetM23());

        lt_atts.SetM30(atts.GetM30());
        lt_atts.SetM31(atts.GetM31());
        lt_atts.SetM32(atts.GetM32());
        lt_atts.SetM33(atts.GetM33());

        lt_atts.SetInvertLinearTransform(atts.GetInvertLinearTransform());
        lt_atts.SetTransformVectors(atts.GetTransformVectors());

        ltf->SetAtts(&lt_atts);
    }
}


// ****************************************************************************
//  Method: avtTransformFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtTransformFilter with the given
//      parameters would result in an equivalent avtTransformFilter.
//
//  Programmer: meredith -- generated by xml2info
//  Creation:   Mon Sep 24 13:29:16 PST 2001
//
// ****************************************************************************

bool
avtTransformFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(TransformAttributes*)a);
}


// ****************************************************************************
//  Method: avtTransformFilter::GetFacadedFilter
//
//  Purpose:
//      Gets the filter we are facading (the similarity transform filter).
//
//  Programmer: Hank Childs
//  Creation:   July 1, 2003
//
//  Modifications:
//
//    Hank Childs, Tue Feb  1 16:37:56 PST 2005
//    Add support for coordinate systems.
//
//    Jeremy Meredith, Tue Apr 15 13:44:21 EDT 2008
//    Added support for linear transforms.
//
// ****************************************************************************

avtFilter *
avtTransformFilter::GetFacadedFilter(void)
{
    if (atts.GetTransformType() == TransformAttributes::Similarity)
        return stf;
    else if (atts.GetTransformType() == TransformAttributes::Coordinate)
        return csc;
    else
        return ltf;
}


// ****************************************************************************
//  Method: avtTransformFilter::GetFacadedFilter
//
//  Purpose:
//      Gets the filter we are facading (the similarity transform filter).
//
//  Programmer: Tom Fogal
//  Creation:   June 23, 2009
//
// ****************************************************************************

const avtFilter *
avtTransformFilter::GetFacadedFilter(void) const
{
    if (atts.GetTransformType() == TransformAttributes::Similarity)
        return stf;
    else if (atts.GetTransformType() == TransformAttributes::Coordinate)
        return csc;
    else
        return ltf;
}

// ****************************************************************************
// Method: avtTransformFilter::UpdateDataObjectInfoCB
//
// Purpose:
//   Update the data object information via a callback function.
//
// Note:       Work partially supported by DOE Grant SC0007548.
//
// Programmer: Brad Whitlock
// Creation:   Tue Mar 18 10:53:05 PDT 2014
//
// Modifications:
//
// ****************************************************************************

void
avtTransformFilter::UpdateDataObjectInfoCB(avtDataObject_p &input,
    avtDataObject_p &output, void *This)
{
    avtDataAttributes &outAtts = output->GetInfo().GetAttributes();
    const TransformAttributes &tf = ((const avtTransformFilter *)This)->atts;

    char tmp[200];
    std::string scale, translate, rotate;
    if(tf.GetDoScale())
    {
        snprintf(tmp, 200, "scale=%lg,%lg,%lg ", 
                 tf.GetScaleX(), tf.GetScaleY(), tf.GetScaleZ());
        scale = tmp;
    }
    if(tf.GetDoTranslate())
    {
        snprintf(tmp, 200, "translate=%lg,%lg,%lg ", 
                 tf.GetTranslateX(), tf.GetTranslateY(), tf.GetTranslateZ());
        translate = tmp;
    }
    if(tf.GetDoRotate())
    {
        snprintf(tmp, 200, "rotateOrigin=%lg,%lg,%lg rotateAxis=%lg,%lg,%lg rotateAmount=%lg", 
                 tf.GetRotateOrigin()[0], tf.GetRotateOrigin()[1], tf.GetRotateOrigin()[2],
                 tf.GetRotateAxis()[0], tf.GetRotateAxis()[1], tf.GetRotateAxis()[2],
                 tf.GetRotateAmount());
        rotate = tmp;
    }
    outAtts.AddFilterMetaData("Transform", scale + translate + rotate);
}
